/*
 *
 *  *    Copyright © OpenAtom Foundation.
 *  *
 *  *    Licensed under the Apache License, Version 2.0 (the "License");
 *  *    you may not use this file except in compliance with the License.
 *  *    You may obtain a copy of the License at
 *  *
 *  *         http://www.apache.org/licenses/LICENSE-2.0
 *  *
 *  *    Unless required by applicable law or agreed to in writing, software
 *  *    distributed under the License is distributed on an "AS IS" BASIS,
 *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  *    See the License for the specific language governing permissions and
 *  *    limitations under the License.
 *
 */

package com.inspur.edp.cef.engine.core.data;


import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.inspur.edp.cef.designtime.api.IGspCommonDataType;
import com.inspur.edp.cef.designtime.api.IGspCommonField;
import com.inspur.edp.cef.engine.core.common.CefDataTypeUtil;
import com.inspur.edp.cef.entity.accessor.base.AccessorBase;
import com.inspur.edp.cef.entity.accessor.base.ReadonlyDataException;
import com.inspur.edp.cef.entity.accessor.dataType.ValueObjAccessor;
import com.inspur.edp.cef.entity.changeset.IChangeDetail;
import com.inspur.edp.cef.entity.entity.ICefData;
import com.inspur.edp.cef.entity.entity.IValueObjData;
import com.inspur.edp.udt.api.UdtManagerUtils;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import lombok.var;

public class EngineValueObjReadonlyAccessor extends
    com.inspur.edp.cef.core.data.ValueObjAccessor {

  private final IGspCommonDataType node;
  private Map<String, Object> nestedValues;

  public EngineValueObjReadonlyAccessor(IGspCommonDataType node, IValueObjData inner) {
    super(inner);
    this.node = node;
    initialize();
  }

  @JsonIgnore
  protected IGspCommonDataType getDataType(){
    return node;
  }

  @Override
  public boolean getIsReadonly() {
    return true;
  }

  @Override
  protected void acceptChangeCore(IChangeDetail iChangeDetail) {
    throw new UnsupportedOperationException();
  }

  @Override
  public Object innerGetValue(String labelId) {
    Objects.requireNonNull(labelId, "labelId");

    var element = CefDataTypeUtil.checkElementExists(node, labelId, getElementPredicate());
    if (element.getIsUdt()) {
      return nestedValues.get(element.getLabelID());
    } else {
      return getInnerData().getValue(labelId);
    }
  }

  @Override
  public void innerSetValue(String s, Object o) {
    throw new ReadonlyDataException();
  }

  @Override
  protected AccessorBase createNewObject() {
    return new EngineValueObjReadonlyAccessor(node, null);
  }

  @Override
  public void copyCore(com.inspur.edp.cef.entity.accessor.base.AccessorBase accessor) {
    EngineValueObjReadonlyAccessor result = (EngineValueObjReadonlyAccessor) accessor;
    if (nestedValues != null) {
      result.nestedValues = new HashMap<>(nestedValues.size());
      nestedValues.entrySet().forEach(pair -> {
        var nestedValue = (ValueObjAccessor) ((ICefData) pair.getValue()).copy();
        nestedValue.setBelongBuffer(result);
        result.nestedValues.put(pair.getKey(), nestedValue);
      });
    }
  }

  @Override
  public ICefData innerCopySelf() {
    ICefData result = (ICefData) super.copySelf();
    if (nestedValues != null) {
      nestedValues.entrySet().forEach(pair -> {
        result.setValue(pair.getKey(), ((ICefData) pair.getValue()).copySelf());
      });
    }
    return result;
  }

  @Override
  protected void onInnerDataChange() {
    super.onInnerDataChange();
    initializeNested();
  }

  private void initialize() {
    initializeNested();
  }

  private void initializeNested() {
    if (getInnerData() == null) {
      return;
    }

    nestedValues = new HashMap<>();
    CefDataTypeUtil.streamElements(node, getElementPredicate()).filter(item -> item.getIsUdt())
        .forEach(item -> {
          String configId = CefDataTypeUtil.getUdtConfigId(item.getUdtID(), item.getUdtPkgName());
          ValueObjAccessor udtValue = (ValueObjAccessor) UdtManagerUtils.getUdtFactory()
              .createManager(configId)
              .getAccessorCreator()
              .createReadonlyAccessor((ICefData) getInnerData().getValue(item.getLabelID()));
          udtValue.setBelongBuffer(this);
          udtValue.setPropName(item.getLabelID());
          nestedValues.put(item.getLabelID(), udtValue);
        });
  }

  //#region virtual
  private Predicate<IGspCommonField> getElementPredicate() {
    return null;
  }

  private Predicate<IGspCommonDataType> getNodePredicate() {
    return null;
  }
  //#endregion
}
